/* Copyright (C) 2016, 2017 Enrico Rossi
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program.
 * If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>
#include <stdio.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <util/delay.h>
#include "lps25.h"
#include "buzz.h"

/*! \file
 *
 * 1mt O2 15C = 0.12hPa
 *
 */

/* globals
 * dms is Deci Meters per Second altitude change.
 * ex. dms = 10 -> 1 m/s climbing up
 */
volatile int8_t dms;
// volatile uint8_t button;

/*! IRQ for both PB3 and PB4.
 * PB4-PCINT4 lps data ready,
 * PB3-PCINT3 reset switch.
 *  generated by a delay enough to trigger
 *  the watchdog-reset.
 *
 * \note It is a level interrupt, meaning which level
 * need to be checked by reading the pin.
 */
ISR(PCINT0_vect)
{
	// Switch pressed
	if (bit_is_clear(PINB, PB3))
		lps25_pressure();

	// Int 4
	if (bit_is_set(PINB, PB4))
		lps25_pressure();
}

/*! IRQ Watchdog vector
 */
ISR(WDT_vect)
{
	if (bit_is_set(PINB, PB4))
		lps25_pressure();
}

/*! Single tone beep
 *
 * Used during initialization.
 */
void beep(uint8_t i)
{
	do {
		buzz_play(3000, 50);
		_delay_ms(200);
		buzz_stop();
		_delay_ms(200);
	} while (--i);
}

/*! Main
 *
 * \note: There is the possibility that
 * the voltage is lower than 1.8V where the
 * MCU is powered but the sensor is not.
 */
int main(void)
{
	uint8_t mute = TRUE;
	uint8_t err;

	// Disable Watchdog (not cleared on reset)
	//
	// \note: maybe it is to late here, need to be
	// disabled in the .init() section.
	wdt_disable();

	/* wait for the capacitor to charge and
	 * the I2C sensor to be ready (0.5s).
	 */
	_delay_ms(1000);

	// Initialize the buzzer
	buzz_init();

	// Initialize the I2C sensor.
	if (lps25_init()) {
		err = TRUE;
	} else {
		lps25_fifo_mean_mode();
		err = FALSE;
	}

	// configure IRQ
	GIMSK |= (1 << PCIE);   // pin change interrupt enable
	PCMSK |= (1 << PCINT4) | (1 << PCINT3); // IRQ ena PB4 and PB3

	// Set sleep mode
	set_sleep_mode(SLEEP_MODE_IDLE);

	// Enable watchdog before IRQ, every second
	// note: IRQ wdt is unsupported by avr-libc
	//
	// Enable Reset watchdog
	//wdt_enable(WDTO_4S);

	// Reset the watchdog
	wdt_reset();
	// Unlock the security 1 WDT change.
	WDTCR = (1<<WDCE)|(1<<WDE);
	// WDT in IRQ @ 1sec.
	WDTCR = (1<<WDIE) | (1<<WDP2) | (1<<WDP1);

	//Enable global interrupts
	sei();

	beep(1);

	if (err)
		beep(5);

	// Clear the IRQ if missed for
	// whatever reason (safeguard)
	if (bit_is_set(PINB, PB4))
		lps25_pressure();

	while(1) {
		// start sleep procedure
		// Wakeup 7 times/sec. depending on
		// the sensor number of samples per second.
		sleep_enable();
		sleep_cpu();
		sleep_disable();

		// Reset the watchdog
		wdt_reset();

		// 1hPa = 8.3m
		// 1m/s = 1/8.3 hPa/s = 0.12 hPa/s
		// 7Hz dms rounded to 0.02 hPa @7Hz
		dms = (-lps25->dHpa) * 500;

		/* About sound:
		 * STATE -> UP, DOWN, FLAT
		 *  UP: beep with changing in freq and period.
		 *  DOWN: sound changing only in freq continuosly.
		 *  FLAT: no sound.
		 *
		 * BEEPS: number of wakeups the sound should continue.
		 *  If 7Hz -> +1ms = 3 cycle sound and 3 cycle no sound.
		 *  Left 1 cycle -> who cares.
		 *  note: if sleep stop sound, then all this is useless.
		 */
		if (((dms > 5) || (dms < -25)) && mute) {
			buzz_play((4000 + dms * 10), 50);
			mute = FALSE;
		} else {
			buzz_stop();
			mute = TRUE;
		}
	}

	return(0);
}
